//######################################################################
//const char *odump_errmsg[];
        .rdata
        .align  2
$msg0:
        .asciiz  "OK"
$msg1:
        .asciiz  "No se pudo leer el archivo\n"
$msg2:
        .asciiz  "No se pudo escribir en el archivo\n"
$msg3:
        .asciiz  "Error al intentar abrir el archivo %s\n"
$msg4:
        .asciiz  "Error en el archivo octal, byte mayor a 255\n"

        .globl  odump_errmsg
        .data
        .type   odump_errmsg, @object
        .size   odump_errmsg, 20
odump_errmsg:
        .word   $msg0
        .word   $msg1
        .word   $msg2
        .word   $msg3
	.word   $msg4

//######################################################################
//void bin2oc(unsigned char bin, char* octal) {

// Diagrama de Stack
// 28   [  a1  ]
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

#include <mips/regdef.h>
#include <sys/syscall.h>

.data
    end:  .ascii  "\0"

.text
.align 2


.globl bin2oc
.ent bin2oc

bin2oc:
	.frame $fp,24,ra
        .set noreorder
        .cpload t9
        .set reorder

	// creo stack frame
        subu sp,sp,24           #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 0

        sw $fp,20(sp)           #Respaldo la posicion del frame pointer
        sw gp,16(sp)            #Respaldo la posicion del global pointer
        move $fp,sp

        sw a0,24(sp)            #bin
        sw a1,28(sp)            #octal

        li t0,2                 #i = 2, contador del loop
        li t1,8                 #divisor = 8, constante
        move t2,a0              #t2 = bin = dividendo

$FOR:
        div t2,t1               #LO = t2/t1 y HI = t2 mod t1
        mflo t2                 #Dividendo = Cociente
        mfhi t3
        addi t3,t3,48           #resto + 48 forma el ascii del numero
        addu t4,a1,t0           #puntero a vector "octal" + offset
        sb t3,0(t4)             #octal[i] = ascii
        addi t0,t0,-1           #Resto 1 al contador
        bgez t0,$FOR

        lw $fp,20(sp)           #Recupero la posicion del frame pointer
        lw gp,16(sp)            #Recupero la posicion del global pointer
        addu sp,sp,24
        j ra
.end bin2oc

//######################################################################
//unsigned char oc2bin(char octal[]) {

// Diagrama de Stack
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl oc2bin
.ent oc2bin

oc2bin:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
        // creo stack frame
        subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 16
    
        sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
        sw a0,24($fp)	# guardo el argumento en el ABA del caller
    
    	lbu t0,0(a0)    # cargo el primer char del octal[3]
    	subu t0,t0,48
        bgt t0,7,oc2bin_error
        blt t0,0,oc2bin_error
    	sll t0,t0,6     # t0 = t0*64 = t0*(2^6) 
        
    	lbu t1,1(a0)    # cargo el segundo char del octal[3]
    	subu t1,t1,48
        bgt t1,7,oc2bin_error
        blt t1,0,oc2bin_error
    	sll t1,t1,3     # t0 = t0*8 = t0*(2^3) 
        
    	lbu t2,2(a0)    # cargo el tercer char del octal[3]
    	subu t2,t2,48
        bgt t2,7,oc2bin_error
        blt t2,0,oc2bin_error
    	sll t2,t2,0     # t0 = t0*1 = t0*(2^0) 
        
    	addu v0,t0,t1
    	addu v0,v0,t2

        blt v0,256,oc2bin_ok	#Verifica que los bytes sean menores que 256  
        
oc2bin_error:
	//hubo error en el byte
	li v1,4			#Cargo el codigo de error
	li v0,0			#Vacio el resultado

oc2bin_ok:
	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end oc2bin

//######################################################################
//Esta funcion es la equivalente a write() de C
//size_t write_asm(int fildes, const void *buf, size_t nbytes);

// Diagrama de Stack
// 32   [  a2  ]
// 28   [  a1  ]
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl write_asm
.ent write_asm

write_asm:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
        // creo stack frame
        subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 16
    
        sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
        sw a0,24($fp)	# guardo el argumento 0 en el ABA del caller
	sw a1,28($fp)	# guardo el argumento 1 en el ABA del caller
	sw a3,32($fp)	# guardo el argumento 2 en el ABA del caller
    
	li v0,SYS_write # Los argumentos ya vienen cargados en el correcto orden desde la llamada
	syscall

	bne a3,zero,write_error	#Ocurrio un error en la escritura
	beq a3,zero,write_exit
    
write_error:

	move v0,zero
	addi v1,zero,2	#Pongo a v1 en 2 para indicar error de escritura

write_exit:
	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end write_asm

//######################################################################
//Esta funcion es la equivalente a read() de C
//ssize_t read_asm(int fildes, void *buf, size_t nbyte);

// Diagrama de Stack
// 32   [  a2  ]
// 28   [  a1  ]
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl read_asm
.ent read_asm

read_asm:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
        // creo stack frame
        subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 16
    
        sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
        sw a0,24($fp)	# guardo el argumento 0 en el ABA del caller
	sw a1,28($fp)	# guardo el argumento 1 en el ABA del caller
	sw a3,32($fp)	# guardo el argumento 2 en el ABA del caller
    
	li v0,SYS_read # Los argumentos ya vienen cargados en el correcto orden desde la llamada
	syscall

	bne a3,zero,read_error	#Ocurrio un error en la lectura
	beq a3,zero,read_exit
    
read_error:

	move v0,zero
	addi v1,zero,1	#Pongo a v1 en 1 para indicar error en la lectura	

read_exit:
	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end read_asm

//######################################################################
//Esta funcion es la equivalente a strlen() de C
//size_t strlen_asm(const char *str);

// Diagrama de Stack
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl strlen_asm
.ent strlen_asm

strlen_asm:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
    // creo stack frame
    subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
    .cprestore 16
    
    sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
    sw a0,24($fp)	#guardo el argumento 0 en el ABA del caller
    
	move v0,zero    #inicializo en cero el retorno
	lb t1,end	#Cargo el "\0"

$LOOP:
	add t2,a0,v0	#inicio de vector + len
	lb t0,0(t2)	#Leo una letra
	addi v0,v0,1	#incrementa la longitud
	bne t0,t1,$LOOP

	addi v0,v0,-1	#Hay que restar 1 al resultado porque no se cuenta al \0 como letra

	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end strlen_asm


//######################################################################
//int odump_encode(int infd, int outfd, struct odump_opts *opts);

// Diagrama de Stack
// 48 - [  a2  ]
// 44 - [  a1  ]
// 40 - [__a0__]_____________________
// 36 - [  pad ]
// 32 - [  ra  ] Saved Register Area
// 28 - [  fp  ]
// 24 - [  gp  ]_____________________
// 20 - [      ] Local Temp Area
// 16 - [      ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl odump_encode
.ent odump_encode

odump_encode:
    .frame $fp,40,ra
	.set noreorder
	.cpload t9
	.set reorder
    
    // creo stack frame
    subu sp,sp,40   #Reservo 40 Bytes (16 para parametros, 16 para respaldar los valores de fp, gp y ra)
    .cprestore 24
    
    sw ra,32(sp)	#Respaldo el ra, la funcion es non-leaf
    sw $fp,28(sp)   #Respaldo la posicion del frame pointer
	sw gp,24(sp)	#Respaldo la posicion del global pointer
    
	move $fp,sp
    
    sw a0,40($fp)	#guardo el argumento 0 en el ABA del caller
    sw a1,44($fp)	#guardo el argumento 1 en el ABA del caller
    sw a2,48($fp)	#guardo el argumento 2 en el ABA del caller
    
    move v1,zero	#inicializo v1 en cero para luego detectar errores
    li t0,0      #inicializo el contador en 0
    sw t0,16($fp)   #lo guardo en la LTA
    
    //leo de infd por primera vez
    lw a0,40($fp)
    la a1,bin_encode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no leyo 1 caracter salgo

$WHILE_ENCODE:
    //paso el binario a octal
    lbu a0,bin_encode(zero)
    la a1,oc_encode
    jal bin2oc
    
    //escribo en outfd
    lw a0,44($fp)
    la a1,oc_encode
    li a2,3
    jal write_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,3
    bne t0,v0,$OUT_ENCODE # si no escribio 3 caracteres salgo
    
    // chequeo si tengo que escribir un salto de linea
    lw t0, 16($fp)  # levanto el contador de bytes octales
    addiu t0,t0,1   # le sumo 1
    sw t0, 16($fp)  # guardo el contador de bytes octales
    lw t1,48($fp)   # levanto el puntero al struct
    lw t2,0(t1)  # leo el valor de length
    bne t0,t2,$DELIM_ENCODE
    
    //escribo un salto de linea
    //antes leo de infd para ver si llegue al eof
    lw a0,40($fp)
    la a1,bin_encode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no leyo 1 caracter salgo

    lw a0,44($fp)
    la a1,endline_encode
    li a2,1
    jal write_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no escribio 1 caracter salgo
    
    li t0,0      #inicializo el contador en 0
    sw t0,16($fp)   #lo guardo en la LTA
    b $WHILE_ENCODE
    
$DELIM_ENCODE:
    //leo de infd para ver si llegue al eof
    lw a0,40($fp)
    la a1,bin_encode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no leyo 1 caracter salgo

    //escribo el delimitador
    lw t0,48($fp)               # levanto el puntero al struct
    lw a0,4(t0)                 # cargo el puntero al delimitador en a0
    beqz a0,$WHILE_ENCODE       # chequeo si la direccion del delimitador es 0 (NULL) 
    jal strlen_asm              # llamo a strlen para obtener el largo del delimitador
    sw v0,20($fp)               # guardo el largo del delimitador
    beqz v0,$WHILE_ENCODE
    lw a0,44($fp)
    lw t0,48($fp)               # levanto el puntero al struct
    lw a1,4(t0)                 # cargo el puntero al delimitador en a1
    addiu a2,v0,0               # cargo el valor devuelto por strlen_asm en a2
    jal write_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    lw t0,20($fp)               # levanto el largo del delimitador
    bne t0,v0,$OUT_ENCODE       # si no escribio el delimitador salgo
    b $WHILE_ENCODE

$ERR_ENCODE:
    move v0,v1 		#Muevo el codigo de error al retorno de la funcion y salgo de esta
    b $OUT_ENCODE

$OUT_ENCODE:
        lw ra,32(sp)	#Recupero la posicion del return address
	lw $fp,28(sp)	#Recupero la posicion del frame pointer
	lw gp,24(sp)	#Recupero la posicion del global pointer
	addu sp,sp,40
	j ra
.end odump_encode

    
    
//######################################################################
//int odump_decode(int infd, int outfd, struct odump_opts *opts);

// Diagrama de Stack
// 56 - [  a2  ]
// 52 - [  a1  ]
// 48 - [__a0__]_____________________
// 44 - [  pad ]
// 40 - [  ra  ] Saved Register Area
// 36 - [  fp  ]
// 32 - [  gp  ]_____________________
// 28 - [      ] 
// 24 - [      ] Local Temp Area
// 20 - [      ] 
// 16 - [      ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl odump_decode
.ent odump_decode

odump_decode:
    .frame $fp,48,ra
	.set noreorder
	.cpload t9
	.set reorder
    
    // creo stack frame
    subu sp,sp,48   #Reservo 40 Bytes (16 para parametros, 16 para respaldar los valores de fp, gp y ra)
    .cprestore 32
    
    sw ra,40(sp)	#Respaldo el ra, la funcion es non-leaf
    sw $fp,36(sp)   #Respaldo la posicion del frame pointer
	sw gp,32(sp)	#Respaldo la posicion del global pointer
    
	move $fp,sp
    
    sw a0,48($fp)	#guardo el argumento 0 en el ABA del caller
    sw a1,52($fp)	#guardo el argumento 1 en el ABA del caller
    sw a2,56($fp)	#guardo el argumento 2 en el ABA del caller
    
    move v1,zero	#inicializo v1 en cero para luego detectar errores
    li t0,0         #inicializo el contador en 0
    sw t0,16($fp)   #lo guardo en la LTA
    
$WHILE_DECODE:
la t0,oc_decode
sb zero,0(t0)
sb zero,1(t0)
sb zero,2(t0)


//leo de infd
	lw a0,48($fp)
    la a1,oc_decode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_DECODE # si no leyo 1 caracteres salgo
    
    lb t0,oc_decode(zero)
    beq t0,10,$WHILE_DECODE # si lei un \n (10 en bytes) sigo
    
    //leo los 2 caracteres que me faltan
	lw a0,48($fp)
    la t0,oc_decode
    addi a1,t0,1    # leo a partir de oc_decode[1]
    li a2,2
    jal read_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,2
    bne t0,v0,$OUT_DECODE # si no leyo 2 caracteres sigo

$SIGO:
    //paso el octal a binario
    la a0,oc_decode
    jal oc2bin
    bne v1,zero,$ERR_DECODE	#Verifica errores
    sw v0,bin_decode
    
    //escribo en outfd
    lw a0,52($fp)
    la a1,bin_decode
    li a2,1
    jal write_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_DECODE # si no escribio 1 caracter salgo
    
$DELIM_DECODE:
    //leo el delimitador
    lw t0,56($fp)           # levanto el puntero al struct
    lw a0,4(t0)             # cargo el puntero al delimitador en a0
    beqz a0,$WHILE_DECODE   # chequeo si la direccion del delimitador es 0 (NULL) 
    jal strlen_asm          # llamo a strlen para obtener el largo del delimitador
    sw v0,20($fp)           # guardo el largo del delimitador
    beqz v0,$WHILE_DECODE   # si el largo es 0 paso
    
    li t0,0
    sw t0,24($fp)
$READ_DELIM:
    lw a0,48($fp)
    la a1,void_decode       # leo en void
    li a2,1                 # leo 1 byte
    jal read_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_DECODE   # si no leyo 1 byte salgo
    
    lw t0,void_decode(zero)
    beq t0,10,$WHILE_DECODE # si lei un \n (10 en bytes) sigo
    
    lw t0,24($fp)           # levanto el contador
    addiu t0,t0,1           # incremento el contador
    sw t0,24($fp)           # guardo el contador incrementado
    lw t1,20($fp)           # levanto el largo del delimitador
    bgt t1,t0,$READ_DELIM
    li t0,0                 # reinicio a 0 el contador
    sw t0,24($fp)           # guardo el contador
    b $WHILE_DECODE

$ERR_DECODE:
    move v0,v1 		#Muevo el codigo de error al retorno de la funcion y salgo de esta
    b $OUT_DECODE

$OUT_DECODE:
    lw ra,40(sp)	#Recupero la posicion del return address
	lw $fp,36(sp)	#Recupero la posicion del frame pointer
	lw gp,32(sp)	#Recupero la posicion del global pointer
	addu sp,sp,48
	j ra
.end odump_decode

.data

    endline_encode: .ascii "\n"
    bin_encode: .byte 0
    oc_encode: .byte 0,0,0,0

    endline_decode: .ascii "\n"
    bin_decode: .byte 0
    oc_decode: .byte 0,0,0
    void_decode: .byte 0
